package com.project_doctor.config;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.zip.GZIPInputStream;
import org.springframework.stereotype.Component;

/**
 * 居民地址地理编码配置类
 * 
 * 主要功能：
 * 1. 将居民地址转换为经纬度坐标
 * 2. 调用高德地图Web服务API进行地理编码
 * 3. 支持家庭医生契约系统的居民定位功能
 * 4. 为前端地图显示提供精确的坐标数据
 * 
 * 技术实现：
 * - 使用原生HttpURLConnection替代RestTemplate
 * - 模拟浏览器请求头绕过反爬虫机制
 * - 支持Gzip压缩响应的自动解压
 * - 采用Jackson进行JSON响应解析
 * 
 * 业务场景：
 * - 居民信息录入时自动获取地址坐标
 * - 地图界面展示居民分布位置
 * - 医生团队服务范围分析
 * - 就近医疗资源推荐
 * 
 * API配置：
 * - API Key: 007c41833023f5c9a0093a8a7bcf1b3d (高德Web服务API)
 * - 接口地址: https://restapi.amap.com/v3/geocode/geo
 * - 响应格式: JSON
 * 
 * @author Project Team
 * @version 1.0
 * @since 2024
 */
@Component
public class RessGeocoderConfig {
    // 高德地图API Key（Web服务API）
    private static final String AMAP_KEY = "007c41833023f5c9a0093a8a7bcf1b3d";
    private static final String GEOCODING_URL = "https://restapi.amap.com/v3/geocode/geo";

    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 根据地址获取经纬度坐标（系统主入口方法）
     * 
     * 业务逻辑：
     * 1. 接收居民地址字符串（如："惠州市惠城区文昌一路9号"）
     * 2. 进行基础数据验证（非空、非空白）
     * 3. 调用高德地图API进行地理编码转换
     * 4. 返回标准经纬度坐标数组格式：[经度, 纬度]
     * 
     * 系统调用场景：
     * - ResidentController.getAllResident()：获取居民列表时自动进行地理编码
     * - 居民信息新增/更新时的地址坐标计算
     * - 地图组件初始化时的批量坐标获取
     * 
     * 坐标系统：
     * - 使用GCJ-02坐标系（火星坐标系）
     * - 适配国内地图显示标准
     * - 与高德地图前端API坐标系统一致
     * 
     * @param address 居民地址字符串（支持详细地址或简化地址）
     * @return 经纬度数组 [longitude, latitude]，失败返回null
     */
    public double[] getCoordinates(String address) {
        if (address == null || address.trim().isEmpty()) {
            return null;
        }
        
        return tryGeocode(address);
    }
    
    /**
     * 执行地理编码API调用（核心实现方法）
     * 
     * 技术实现流程：
     * 1. 地址编码：对中文地址进行UTF-8 URL编码
     * 2. 构建请求：组装高德API请求URL
     * 3. 模拟浏览器：设置完整的HTTP请求头避免反爬虫
     * 4. 发送请求：使用HttpURLConnection进行GET请求
     * 5. 响应处理：处理HTTP状态码和响应流
     * 6. 解压数据：自动检测和解压Gzip压缩响应
     * 7. JSON解析：使用Jackson解析API返回数据
     * 8. 坐标提取：从geocodes数组中提取经纬度
     * 
     * 反爬虫策略：
     * - User-Agent：模拟Chrome浏览器标识
     * - Accept系列：设置标准浏览器接受类型
     * - Referer：设置高德官网来源
     * - Connection：保持连接状态
     * - Cache-Control：禁用缓存
     * 
     * 异常处理：
     * - 网络连接超时：10秒连接，30秒读取
     * - API错误响应：状态码非1时返回null
     * - 数据解析异常：JSON格式错误时返回null
     * - 坐标格式错误：经纬度解析失败时返回null
     * 
     * 性能优化：
     * - 支持Gzip压缩减少传输数据量
     * - 连接复用提高请求效率
     * - 及时释放连接资源
     * 
     * @param address 原始地址字符串
     * @return 成功时返回[经度, 纬度]数组，失败返回null
     */
    private double[] tryGeocode(String address) {
        try {
            // 1. 地址URL编码，处理中文字符
            String encodedAddress = URLEncoder.encode(address, "UTF-8");
            String urlString = GEOCODING_URL + "?address=" + encodedAddress + "&output=JSON&key=" + AMAP_KEY;

            // 2. 建立HTTP连接
            URL url = new URL(urlString);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            
            // 3. 设置请求方法
            connection.setRequestMethod("GET");
            
            // 4. 设置关键请求头，模拟真实浏览器环境
            connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36");
            connection.setRequestProperty("Accept", "application/json, text/plain, */*");
            connection.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
            connection.setRequestProperty("Accept-Encoding", "gzip, deflate, br");
            connection.setRequestProperty("Connection", "keep-alive");
            connection.setRequestProperty("Cache-Control", "no-cache");
            connection.setRequestProperty("Pragma", "no-cache");
            connection.setRequestProperty("Referer", "https://lbs.amap.com/");
            
            // 5. 设置超时时间防止长时间等待
            connection.setConnectTimeout(10000); // 10秒连接超时
            connection.setReadTimeout(30000);    // 30秒读取超时
            
            // 6. 发送请求并获取响应状态
            int responseCode = connection.getResponseCode();
            
            // 7. 获取响应输入流（成功或错误）
            InputStream inputStream = responseCode == 200 ? connection.getInputStream() : connection.getErrorStream();
            
            // 8. 检测并处理Gzip压缩响应
            String encoding = connection.getContentEncoding();
            if ("gzip".equalsIgnoreCase(encoding)) {
                inputStream = new GZIPInputStream(inputStream);
            }
            
            // 9. 读取响应数据
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            reader.close();
            connection.disconnect();
            
            // 10. 解析JSON响应数据
            String responseBody = response.toString();
            JsonNode jsonNode = objectMapper.readTree(responseBody);
            String status = jsonNode.get("status").asText();
            
            // 11. 检查API调用状态并提取坐标
            if ("1".equals(status)) {
                JsonNode geocodes = jsonNode.get("geocodes");
                if (geocodes.isArray() && geocodes.size() > 0) {
                    JsonNode firstResult = geocodes.get(0);
                    String location = firstResult.get("location").asText();
                    String[] coords = location.split(",");
                    if (coords.length == 2) {
                        double lng = Double.parseDouble(coords[0]); // 经度
                        double lat = Double.parseDouble(coords[1]); // 纬度
                        return new double[]{lng, lat};
                    }
                }
            }
            
            // 12. API调用失败或数据格式错误
            return null;
        } catch (Exception e) {
            System.err.println("地理编码异常: " + e.getMessage());
            return null;
        }
    }
}
